package mylab.starters.uuid.model.impl;

import mylab.starters.uuid.exception.IdException;
import mylab.starters.uuid.model.*;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * <p>
 * 区间范围主键默认生成器，根据业务名称自增
 * </p>
 *
 * @date Created in 2019/10/29 16:13
 */
public class DefaultRangeId implements RangeId {
    /**
     * 获取区间是加一把独占锁防止资源冲突
     */
    private final Lock lock = new ReentrantLock();

    /**
     * 序列号区间管理器
     */
    private RangeManager rangeManager;

    /**
     * 当前序列号区间
     */
    private volatile IdRange currentRange;

    /**
     * 需要获取区间的业务名称
     */
    private BusinessName businessName;

    /**
     * 返回主键携带的前缀
     */
    private Prefix prefix;

    @Override
    public long nextId() throws IdException {
        return nextId(this.businessName);
    }

    /**
     * （根据业务名称）生成下一个主键
     *
     * @param businessName 业务名称
     * @return 主键
     * @throws IdException 主键生成异常
     */
    @Override
    public long nextId(BusinessName businessName) throws IdException {
        String name = businessName.get();
        //当前区间不存在，重新获取一个区间
        if (null == currentRange) {
            lock.lock();
            try {
                if (null == currentRange) {
                    currentRange = rangeManager.nextRange(name);
                }
            } finally {
                lock.unlock();
            }
        }

        //当value值为-1时，表明区间的序列号已经分配完，需要重新获取区间
        long value = currentRange.getAndIncrement();
        if (value == -1) {
            lock.lock();
            try {
                for (; ; ) {
                    if (currentRange.isOver()) {
                        currentRange = rangeManager.nextRange(name);
                    }

                    value = currentRange.getAndIncrement();
                    if (value == -1) {
                        continue;
                    }

                    break;
                }
            } finally {
                lock.unlock();
            }
        }

        if (value < 0) {
            throw new IdException("Id value overflow, value = " + value);
        }

        return value;
    }

    /**
     * 生成下一个主键(带格式)
     *
     * @return 主键(带格式)
     * @throws IdException 主键生成异常
     */
    @Override
    public String nextIdStr() throws IdException {
        return nextIdStr(this.businessName, this.prefix);
    }

    /**
     * （根据业务名称）生成下一个主键(带格式)
     *
     * @param businessName 业务名称
     * @param prefix       前缀
     * @return 主键(带格式)
     * @throws IdException 主键生成异常
     */
    @Override
    public String nextIdStr(BusinessName businessName, Prefix prefix) throws IdException {
        return String.format("%s%05d", prefix.get(), nextId(businessName));
    }

    @Override
    public void setRangeManager(RangeManager rangeManager) {
        this.rangeManager = rangeManager;
    }

    /**
     * 设置主键获取名称
     *
     * @param prefix 前缀
     */
    @Override
    public void setPrefix(Prefix prefix) {
        this.prefix = prefix;
    }

    @Override
    public void setBusinessName(BusinessName businessName) {
        this.businessName = businessName;
    }
}
